/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package org.mule.runtime.config.spring.parsers;

import org.mule.runtime.config.spring.parsers.assembly.BeanAssembler;
import org.mule.runtime.config.spring.parsers.generic.AutoIdUtils;
import org.mule.runtime.config.spring.util.SpringXMLUtils;
import org.mule.runtime.core.util.StringUtils;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

/**
 * This definition parser supports the definition of beans that are then set on the parent bean - it extends
 * {@link org.mule.runtime.config.spring.parsers.AbstractHierarchicalDefinitionParser} with methods that assume the data are
 * associated with a single property.
 *
 * This supports collections and Maps. For collections if a child element is repeated it will be assumed that it is a collection.
 *
 * If the Bean Class for this element is set to
 * {@link org.mule.runtime.config.spring.parsers.collection.ChildMapEntryDefinitionParser.KeyValuePair} it is assumed that a Map
 * is being processed and any child elements will be added to the parent Map. Similarly for
 * {@link org.mule.runtime.config.spring.parsers.collection.ChildListEntryDefinitionParser}.
 *
 * A single method needs to be overriden called {@link #getPropertyName} that determines the name of the property to set on the
 * parent bean with this bean. Note that the property name can be dynamically resolved depending on the parent element.
 *
 * @see org.mule.runtime.config.spring.parsers.generic.ChildDefinitionParser
 * @see org.mule.runtime.config.spring.parsers.collection.ChildMapEntryDefinitionParser.KeyValuePair
 * @see AbstractMuleBeanDefinitionParser
 */
public abstract class AbstractChildDefinitionParser extends AbstractHierarchicalDefinitionParser
    implements MuleChildDefinitionParser {

  protected final void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    setRegistry(parserContext.getRegistry());
    parseChild(element, parserContext, builder);
  }

  protected void parseChild(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    builder.setScope(isSingleton() ? BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
    super.doParse(element, parserContext, builder);
  }

  protected void postProcess(ParserContext context, BeanAssembler assembler, Element element) {
    super.postProcess(context, assembler, element);

    // legacy handling of orphan beans - avoid setting parent
    String propertyName = getPropertyName(element);
    if (null != propertyName) {
      // If this is a singleton we need to inject it into parent using a
      // RuntimeBeanReference so that the bean does not get created twice, once
      // with a name and once as an (inner bean).
      if (!assembler.getBean().getBeanDefinition().isSingleton()) {
        assembler.insertBeanInTarget(propertyName);
      } else {
        assembler.insertSingletonBeanInTarget(propertyName,
                                              element.getAttribute(AbstractMuleBeanDefinitionParser.ATTRIBUTE_NAME));
      }
    }
  }

  public String getBeanName(Element e) {
    String name = SpringXMLUtils.getNameOrId(e);
    if (StringUtils.isBlank(name)) {
      String parentId = getParentBeanName(e);
      if (!parentId.startsWith(".")) {
        parentId = "." + parentId;
      }
      return AutoIdUtils.uniqueValue(parentId + ":" + e.getLocalName());
    } else {
      return name;
    }
  }

  public abstract String getPropertyName(Element element);

}
